1 module hip.jni.helper.jnicall; 2 import hip.util.conv:to; 3 import hip.util.format; 4 import hip.util.string; 5 import std.traits : isArray; 6 import hip.jni.jni; 7 8 version(Android): 9 10 11 enum javaRep = 12 [ 13 "byte" : "B", 14 "ubyte" : "B", 15 "char" : "C", 16 "bool" : "Z", 17 "int" : "I", 18 "uint" : "I", 19 "void" : "V", 20 "long" : "J", 21 "ulong" : "J", 22 "float" : "F", 23 "double": "D", 24 "short" : "S", 25 "ushort": "S", 26 "string": "Ljava/lang/String;", 27 "Object": "Ljava/lang/Object;" 28 ]; 29 30 enum javaGetTypeRepresentation(T)() 31 { 32 static if(isArray!T && !is(T == string)) 33 { 34 return "["~javaGetTypeRepresentation!(typeof(T.init[0])); 35 } 36 else 37 { 38 static assert((T.stringof in javaRep) !is null, "Type name "~T.stringof~" not found when searching for java type representation"); 39 return javaRep[T.stringof]; 40 } 41 } 42 43 string javaGetType(T)() 44 { 45 long ind = T.stringof.countUntil("["); 46 string ret = T.stringof; 47 if(ind != -1) 48 ret = ret[0..ind]; 49 switch(ret) 50 { 51 case "string": 52 ret = "Ljava/lang/String;"; 53 break; 54 case "Object": 55 ret = "LJava/lang/Object;"; 56 break; 57 case "uint": 58 ret = "int"; 59 break; 60 case "bool": 61 ret~= "ean"; 62 break; 63 case "ushort": 64 ret = "short"; 65 break; 66 case "ulong": 67 ret = "long"; 68 break; 69 default:break; 70 } 71 return ret; 72 } 73 74 private string getArgs(Args...)() 75 { 76 string s; 77 static foreach(i, a; Args) 78 { 79 static if(i < Args.length - 1) 80 s~= a.stringof~","; 81 else 82 s~= a.stringof; 83 } 84 return (s.length > 0 ? (","~s) : "") ; 85 } 86 87 string javaGetClassPath(string path) 88 { 89 string className; 90 91 bool hasClass = false; 92 93 for(int i = 0; i < path.length; i++) 94 { 95 if(path[i] == '.') 96 { 97 if(hasClass) 98 break; 99 className~="/"; 100 } 101 else 102 { 103 if(isUpperCase(path[i])) 104 hasClass = true; 105 className~=path[i]; 106 } 107 } 108 return className; 109 } 110 111 jclass javaGetClass(JNIEnv* env, string path) 112 { 113 path = javaGetClassPath(path); 114 if(path == "" || env == null) 115 return null; 116 return (*env).FindClass(env, path.toStringz); 117 } 118 string javaGetMethodName(string where) 119 { 120 long ind = lastIndexOf(where, '.'); 121 bool isMethodOnly = ind == -1; 122 123 if(isMethodOnly) 124 return where; 125 else 126 return where[ind+1..$]; 127 } 128 string javaGenerateMethodName(alias javaPackage)(string method) 129 { 130 string packName; 131 string pName = javaPackage._packageName; 132 for(ulong i = 0; i < pName.length; i++) 133 { 134 if(pName[i] == '.') 135 packName~= '_'; 136 else if(pName[i] == '_') 137 packName~= "_1"; 138 else 139 packName~= pName[i]; 140 } 141 return "Java_"~packName~"_"~method; 142 } 143 144 enum D_TO_JAVA_TYPE_TABLE = 145 [ 146 "ubyte" : "jboolean", 147 "byte" : "jbyte", 148 "ushort" : "jchar", 149 "short" : "jshort", 150 "int" : "jint", 151 "long" : "jlong", 152 "float" : "jfloat", 153 "double" : "jdouble", 154 "string" : "jstring" 155 ]; 156 157 D javaTypeToD(D, J)(JNIEnv* env, J value) 158 { 159 enum javaType = javaGetType!D; 160 enum javaTypeUpper = toUpper(javaType[0]) ~ javaType[1..$]; 161 162 static if(is(D == Object)) 163 return cast(void*)value; 164 else static if(is(D == string)) 165 { 166 const(char)* chs = (*env).GetStringUTFChars(env, value, null); 167 string str = to!string(chs); 168 (*env).ReleaseStringUTFChars(env, value, chs); 169 return str; 170 } 171 else static if(!isArray!D) 172 return cast(D)value; 173 else 174 { 175 jarray obj = value; 176 177 mixin(format!q{ 178 j%s* javaArr = (*env).Get%sArrayElements(env, cast(j%sArray)obj, null); 179 }(javaType, javaTypeUpper, javaType)); 180 int arrL = (*env).GetArrayLength(env, obj); 181 D ret; 182 alias D_singleType = typeof(D.init[0]); 183 static if(D.length == 0) 184 ret.length = arrL; 185 186 for(int i = 0; i < arrL; i++) 187 ret[i] = cast(D_singleType)javaArr[i]; 188 189 mixin(format!q{(*env).Release%sArrayElements(env, obj, javaArr, 0);}(javaTypeUpper)); 190 return ret; 191 } 192 } 193 194 string java_dType_to_javaType(string dType)() 195 { 196 static assert((dType in D_TO_JAVA_TYPE_TABLE) != null, "Type "~dType~" not found in D_TO_JAVA table"); 197 return D_TO_JAVA_TYPE_TABLE[dType]; 198 } 199 200 string java_convertParameterToD(string typeName, string varName)() 201 { 202 enum isTypeArray = typeName.countUntil("[") != -1; 203 204 static if(typeName.countUntil("string") != -1) 205 return format!q{javaTypeToD!string(env, %s)}(varName); 206 else static if(isTypeArray) 207 return format!q{javaTypeToD!(%s)(env, %s)}(typeName, varName); 208 else 209 return varName; 210 } 211 212 string java_getParametersDef(string funcParams)() 213 { 214 string ret = ""; 215 enum string[] argsSplit = funcParams.split(","); 216 static foreach(i, v; argsSplit) 217 { 218 if(i != 0) 219 ret~=","; 220 ret~= java_dType_to_javaType!(argsSplit[i].split(" ")[0..$-1].join) //jstring, jboolean 221 ~ " "~argsSplit[i].split(" ")[$-1]; //Variables names is the last after the split 222 } 223 return ret; 224 } 225 226 string java_getParametersCall(string funcParams)() 227 { 228 string paramsCall = ""; 229 enum string[] paramsTemp = funcParams.split(","); 230 231 static foreach(i, v; paramsTemp) 232 { 233 static if(i != 0) 234 paramsCall~=","; 235 mixin(format!q{enum string[] typeAndName_%s = v.split(" ");}(i)); 236 mixin(format!q{enum string type_%s = typeAndName_%s[0..$-1].join;}(i, i)); 237 mixin(format!q{enum string name_%s = typeAndName_%s[$-1];}(i, i)); 238 239 //Last string after space is the argument name 240 paramsCall~= java_convertParameterToD!( 241 mixin(format!q{type_%s}(i)), //Get type_0, type_1, type_n 242 mixin(format!q{name_%s}(i)) ////Get name_0, name_1, name_n 243 ); 244 } 245 return paramsCall; 246 } 247 248 249 /** 250 * This function should provide the module to import hip.for it being able to get the type. 251 * Currently only supports basic types 252 */ 253 string javaGenerateMethod(alias javaPackage, string funcSymbol, string m = __MODULE__)() 254 { 255 static assert(__traits(hasMember, javaPackage, "_packageName"), "JavaFunc error: "~javaPackage.stringof~" is not a java package"); 256 mixin("import "~m~";"); 257 258 259 enum metName = javaGenerateMethodName!(javaPackage)(funcSymbol); 260 enum funcData = typeof(mixin(funcSymbol)).stringof; 261 262 enum firstParensIndex = funcData.countUntil("("); 263 enum string funcRet = funcData[0..firstParensIndex]; 264 enum string funcParams = funcData[firstParensIndex+1..$-1]; 265 266 enum string funcParamsConverted = java_getParametersDef!(funcParams); 267 enum string paramsCall = java_getParametersCall!(funcParams); 268 269 270 271 return format!q{ 272 export extern(C) %s %s (JNIEnv* env, jclass clazz, %s) 273 { 274 pragma(inline, true) 275 %s(%s); 276 } 277 }(funcRet, metName, funcParamsConverted, funcSymbol, paramsCall); 278 } 279 280 /** 281 * 282 * It would pretty much work as if extern(Java) existed. Generates the java side names function 283 * definitions for functions marked with @JavaFunc 284 * Don't yet support functions with underlines (I think that the mangling is _1, but need to test) 285 */ 286 mixin template javaGenerateModuleMethodsForPackage(alias javaPackage, alias module_, bool showGeneration = false) 287 { 288 ///Must mix the hasUDA trait. 289 import std.traits:hasUDA; 290 static foreach(m; __traits(allMembers, module_)) 291 { 292 static if(hasUDA!(mixin(m), JavaFunc!javaPackage)) 293 { 294 static if(showGeneration) 295 { 296 pragma(msg, m,":"); 297 pragma(msg, javaGenerateMethod!(javaPackage, m)); 298 } 299 mixin(javaGenerateMethod!(javaPackage, m)); 300 } 301 } 302 } 303 304 /** 305 * By not using templated struct, no instantiation will be required, and it will 306 * be easier for searching when using __traits 307 */ 308 struct JavaFunc_{string packageName;} 309 310 enum JavaFunc(alias T)() 311 { 312 static assert(__traits(hasMember, T, "_packageName"), "JavaFunc error: "~T.stringof~" is not a java package"); 313 return JavaFunc_(T._packageName); 314 } 315 316 template javaGetPackage(string packageName) 317 { 318 immutable(string) _packageName = packageName; 319 320 321 322 auto javaCall(T, string path, Args...)() 323 { 324 JNIEnv* env = _env; 325 static if(is(T == void*)) 326 return javaCall!(Object, path, Args); 327 static if(packageName[$-1] != '.' && path[0] != '.') 328 enum where = packageName~"."~path; 329 else 330 enum where = packageName~path; 331 enum s = getArgs!Args; 332 string typeRepresentation = "("; 333 334 enum javaType = javaGetType!T; 335 enum javaTypeUpper = toUpper(javaType[0]) ~ javaType[1..$]; 336 337 338 foreach (i, a; Args) 339 { 340 typeRepresentation~= javaGetTypeRepresentation!(typeof(a)); 341 if(i < Args.length - 1) 342 typeRepresentation~=","; 343 } 344 typeRepresentation~=")"~javaGetTypeRepresentation!T; 345 346 347 jclass cls = javaGetClass(env, where); 348 jmethodID id = (*env).GetStaticMethodID(env, cls, 349 javaGetMethodName(where).toStringz, typeRepresentation.toStringz); 350 351 static if(is(T == Object)) 352 { 353 jobject obj = mixin(q{(*env).CallStaticObjectMethod(env, cls, id } ~s ~")"); 354 return cast(void*)obj; 355 } 356 else static if(is(T == string)) 357 { 358 jstring obj = mixin(q{(*env).CallStaticObjectMethod(env, cls, id } ~ s~")"); 359 return javaTypeToD!T(env, obj); 360 } 361 else static if(!isArray!T) 362 return cast(T)mixin(q{(*env).CallStatic}~javaTypeUpper~q{Method(env, cls, id } ~s~")"); 363 else 364 { 365 jarray obj = mixin(q{(*env).CallStaticObjectMethod(env, cls, id } ~ s~")"); 366 return javaTypeToD!T(env, obj); 367 } 368 } 369 370 } 371 372 373 // mixin template ExportJavaFuncs(alias module_, alias javaPackage) 374 // { 375 376 // } 377 378 void JNISetEnv(JNIEnv* env){_env = env;} 379 private __gshared JNIEnv* _env = null;